package com.mvc.server.miaosha.access;

import java.io.OutputStream;
import java.util.Objects;
import java.util.stream.Stream;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.alibaba.fastjson.JSON;
import com.mvc.server.component.RedisService;
import com.mvc.server.miaosha.common.CommonContant;
import com.mvc.server.miaosha.pojo.MiaoshaUser;
import com.mvc.server.miaosha.redis.AccessKey;
import com.mvc.server.miaosha.service.MiaoshaUserService;
import com.mvc.server.result.CommonResult;
import com.mvc.server.result.ResultCode;

@Service
public class AccessInterceptor  extends HandlerInterceptorAdapter{
	
	@Resource
	private MiaoshaUserService miaoshaUserService;
	
	@Resource
	private RedisService redisService;
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		if(handler instanceof HandlerMethod) {
			MiaoshaUser user = getUser(request, response);
			UserContext.setUser(user);
			HandlerMethod hm = (HandlerMethod)handler;
			AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
			if(accessLimit == null) {
				return true;
			}
			int seconds = accessLimit.seconds();
			int maxCount = accessLimit.maxCount();
			boolean needLogin = accessLimit.needLogin();
			String key = request.getRequestURI();
			if(needLogin) {
				if(user == null) {
					render(response, ResultCode.USER_SEEEION_ERROR);
					return false;
				}
				key += "_" + user.getId();
			}else {
				//do nothing
			}
			AccessKey ak = AccessKey.withExpire(seconds);
			Integer count = redisService.get(ak, key, Integer.class);
	    	if(count  == null) {
	    		 redisService.set(ak, key, 1);
	    	}else if(count < maxCount) {
	    		 redisService.incr(ak, key, 1);
	    	}else {
	    		render(response, ResultCode.ACCESS_LIMIT_REACHED);
	    		return false;
	    	}
		}
		return true;
	}
	
	private void render(HttpServletResponse response, ResultCode rc)throws Exception {
		response.setContentType("application/json;charset=UTF-8");
		OutputStream out = response.getOutputStream();
		String str  = JSON.toJSONString(CommonResult.error(rc));
		out.write(str.getBytes("UTF-8"));
		out.flush();
		out.close();
	}

	private MiaoshaUser getUser(HttpServletRequest request, HttpServletResponse response) {
		String paramToken = request.getParameter(CommonContant.COOKIE_NAME_TOKEN);
		
		String cookieToken = getCookieValue(request, CommonContant.COOKIE_NAME_TOKEN);
		
		if (StringUtils.isBlank(paramToken) && StringUtils.isBlank(cookieToken)) {
			return null;
		}
		
		String token = StringUtils.isNotBlank(cookieToken) ? cookieToken : paramToken;
		
		return miaoshaUserService.getUserByToken(response, token);
	}
	
	/**
	 * Description:
	 * 		method description here
	 * @param request
	 * @param cookieName
	 * @return String      
	 * @throws                                 
	 */
	private String getCookieValue(HttpServletRequest request, String cookieName) {
		
		Cookie[] cookies = request.getCookies();
		if (Objects.isNull(cookies) || cookies.length == 0) {
			return null;
		}
		
		return Stream.of(cookies)
				.filter(re -> Objects.equals(cookieName, re.getName()))
				.findFirst().map(Cookie::getValue).orElse(null);
	}
}
